use std::convert::TryFrom;
use std::fmt;
use std::str::FromStr;

#[derive(Debug, PartialEq, Eq, Clone)]

pub struct ChunkType{
   bytes:[u8; 4],
}

impl ChunkType {
    /// 返回底层 4 字节
    pub fn bytes(&self) -> [u8; 4] {
        self.bytes
    }

    /// 判断是否所有字节均为 ASCII 字母
    fn is_ascii_letter(c: u8) -> bool {
        c.is_ascii_alphabetic()
    }
    /// 完整的合法性校验：4 字节都要是 ASCII 字母，且保留位（第 3 个字节）必须为大写
    pub fn is_valid(&self) -> bool {
        // 先检查是否全部都是 ASCII 字母
        if !self.bytes.iter().all(|&b| ChunkType::is_ascii_letter(b)) {
            return false;
        }
        // 再检查保留位（第三个字节）是否为大写
        self.is_reserved_bit_valid()
    }
 /// 第 1 字节大写 => critical；否则 ancillary
    pub fn is_critical(&self) -> bool {
        self.bytes[0].is_ascii_uppercase()
    }

    /// 第 2 字节大写 => public；否则 private
    pub fn is_public(&self) -> bool {
        self.bytes[1].is_ascii_uppercase()
    }

    /// 保留位（第 3 个字节）必须大写，否则无效
    pub fn is_reserved_bit_valid(&self) -> bool {
        self.bytes[2].is_ascii_uppercase()
    }

    /// 第 4 字节小写 => safe to copy；第 4 字节大写 => unsafe to copy
    pub fn is_safe_to_copy(&self) -> bool {
        self.bytes[3].is_ascii_lowercase()
    }
}

impl TryFrom<[u8; 4]> for ChunkType {
    // 使用与主程序一致的 Error 别名
    type Error = crate::Error;

    fn try_from(bytes: [u8; 4]) -> Result<Self, Self::Error> {
        if !bytes.iter().all(|&b| b.is_ascii_alphabetic()) {
            return Err(format!(
                "Invalid chunk type: byte array {:?} contains non-ASCII-letter",
                bytes
            ).into());
        }
        Ok(ChunkType { bytes })
    }
}

impl FromStr for ChunkType {
    type Err = crate::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() != 4 {
            return Err(format!("Chunk type string must be 4 characters, got {}", s.len()).into());
        }
        if !s.bytes().all(|b| b.is_ascii_alphabetic()) {
            return Err(format!(
                "Invalid chunk type string: contains non-ASCII-letter: {}",
                s
            ).into());
        }
        let b_array = <[u8; 4]>::try_from(s.as_bytes())
            .map_err(|_| "Failed to convert to byte array".to_string())?;
        Ok(ChunkType { bytes: b_array })
    }
}

/// 实现 Display，让 `format!("{}", chunk)` 输出原始 4 字符串
impl fmt::Display for ChunkType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // 由于前面已保证 bytes 全是 ASCII 字母，这里转换必然成功
        let s = std::str::from_utf8(&self.bytes).map_err(|_| fmt::Error)?;
        write!(f, "{}", s)
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use std::convert::TryFrom;
    use std::str::FromStr;

    #[test]
    pub fn test_chunk_type_from_bytes() {
        let expected = [82, 117, 83, 116];
        let actual = ChunkType::try_from([82, 117, 83, 116]).unwrap();
        assert_eq!(expected, actual.bytes());
    }

    #[test]
    pub fn test_chunk_type_from_str() {
        let expected = ChunkType::try_from([82, 117, 83, 116]).unwrap();
        let actual = ChunkType::from_str("RuSt").unwrap();
        assert_eq!(expected, actual);
    }

    #[test]
    pub fn test_chunk_type_is_critical() {
        let chunk = ChunkType::from_str("RuSt").unwrap();
        assert!(chunk.is_critical());
    }

    #[test]
    pub fn test_chunk_type_is_not_critical() {
        let chunk = ChunkType::from_str("ruSt").unwrap();
        assert!(!chunk.is_critical());
    }

    #[test]
    pub fn test_chunk_type_is_public() {
        let chunk = ChunkType::from_str("RUSt").unwrap();
        assert!(chunk.is_public());
    }

    #[test]
    pub fn test_chunk_type_is_not_public() {
        let chunk = ChunkType::from_str("RuSt").unwrap();
        assert!(!chunk.is_public());
    }

    #[test]
    pub fn test_chunk_type_is_reserved_bit_valid() {
        let chunk = ChunkType::from_str("RuSt").unwrap();
        assert!(chunk.is_reserved_bit_valid());
    }

    #[test]
    pub fn test_chunk_type_is_reserved_bit_invalid() {
        let chunk = ChunkType::from_str("Rust").unwrap();
        assert!(!chunk.is_reserved_bit_valid());
    }

    #[test]
    pub fn test_chunk_type_is_safe_to_copy() {
        let chunk = ChunkType::from_str("RuSt").unwrap();
        assert!(chunk.is_safe_to_copy());
    }

    #[test]
    pub fn test_chunk_type_is_unsafe_to_copy() {
        let chunk = ChunkType::from_str("RuST").unwrap();
        assert!(!chunk.is_safe_to_copy());
    }

    #[test]
    pub fn test_valid_chunk_is_valid() {
        let chunk = ChunkType::from_str("RuSt").unwrap();
        assert!(chunk.is_valid());
    }

    #[test]
    pub fn test_invalid_chunk_is_valid() {
        let chunk = ChunkType::from_str("RuSt").unwrap();
        assert!(chunk.is_valid());

        let chunk = ChunkType::from_str("Rust").unwrap();
        assert!(!chunk.is_valid());

        let chunk = ChunkType::from_str("Ru1t");
        assert!(chunk.is_err());
    }

    #[test]
    pub fn test_chunk_type_string() {
        let chunk = ChunkType::from_str("RuSt").unwrap();
        assert_eq!(&chunk.to_string(), "RuSt");
    }

    #[test]
    pub fn test_chunk_type_trait_impls() {
        let chunk_type_1: ChunkType = TryFrom::try_from([82, 117, 83, 116]).unwrap();
        let chunk_type_2: ChunkType = FromStr::from_str("RuSt").unwrap();
        let _chunk_string = format!("{}", chunk_type_1);
        let _are_chunks_equal = chunk_type_1 == chunk_type_2;
    }
}
